<?php
/**
 * Ruth Library
 * $Id: Channel.php 2 2008-01-17 02:59:56Z baron314159@yahoo.com $
 *
 * LICENSE
 *
 * Copyright (c) 2007, Chris Utz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Impact Lab nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CHRIS UTZ ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL CHRIS UTZ BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @category   Ruth
 * @package    Ruth_Channel
 * @author     Chris Utz <chris@chrisutz.com>
 * @copyright  2007 Chris Utz <chris@chrisutz.com>
 * @license    http://www.opensource.org/licenses/bsd-license.php    New BSD License
 * @version    $Revision: 2 $
 */

/**
 * @see Ruth_Channel_Exception
 */
require_once 'Ruth/Channel/Exception.php';

/**
 * @see Ruth_Channel_Field
 */
require_once 'Ruth/Channel/Field.php';

/**
 * The main class of Ruth library
 *
 * @category   Ruth
 * @package    Ruth_Channel
 * @author     Chris Utz <chris@chrisutz.com>
 * @copyright  2007 Chris Utz <chris@chrisutz.com>
 * @license    http://www.opensource.org/licenses/bsd-license.php    New BSD License
 * @version    $Revision: 2 $
 */
class Ruth_Channel
{
    /**
     * An array object of Ruth_Channel_Field instances.
     *
     * @var ArrayObject
     */
    protected $_fields;

    public function __construct()
    {
        $this->_fields = new ArrayObject();
    }

    /**
     * Registers a field to the channel, creating and returning a 
     * Ruth_Channel_Field object, or just returning one if the specified field
     * was previously registered. Note the type parameter is ignored if the 
     * field was previously registered in the channel.
     *
     * @param string $name The field name to register
     * @param string $type The type of the field
     * @return Ruth_Channel_Field
     */
    public function field($fieldName, $type=Ruth_Channel_Field::TYPE_STRING)
    {
        if (!isset($this->_fields[$fieldName])) {
            $field = new Ruth_Channel_Field($type);
            $field->setFields($this->_fields)->setName($fieldName);

            $this->_fields[$fieldName] = $field;
            return $field;
        } else {
            return $this->_fields[$fieldName];
        }
    }

    /**
     * Registers multiple fields to the channel at the same time, returning
     * a Ruth_Channel_FieldGroup object, allowing fields to be setup in bulk.
     * If the first parameter is a string, it is assumed to be a PCRE, and
     * the returned field group will contain all existing fields matching the
     * regex (type is ignored).
     *
     * @param string|array $whichFields
     * @param string $type
     * @return Ruth_Channel_FieldGroup
     */
    public function fields($whichFields, $type=Ruth_Channel_Field::TYPE_STRING)
    {
        $fields = array();

        if (is_array($whichFields)) {
            foreach ($whichFields as $fieldName) {
                $fields[$fieldName] = $this->field($fieldName, $type);
            }
        } else {
            $regex = strval($whichFields);

            foreach ($this->_fields as $name => $field) {
                if (preg_match($regex, $name)) {
                    $fields[$name] = $field;
                }
            }
        }

        return new Ruth_Channel_FieldGroup($fields);
    }

    /**
     * Fetches a Ruth_Channel_Field instance registered with this channel.
     *
     * @var string $name
     * @return Ruth_Channel_Field
     * @throws Ruth_Channel_Exception
     */
    public function __get($name)
    {
        if (!isset($this->_fields[$name])) {
            throw new Ruth_Channel_Exception("field {$name} not registered");
        }

        return $this->_fields[$name];
    }

    /**
     * Checks if a particular field is registered in this channel.
     *
     * @var string $name
     * @return boolean
     */
    public function __isset($name)
    {
        return isset($this->_fields[$name]);
    }

    /**
     * Sets the field values and runs the filters and validators on the 
     * registered fields.
     *
     * @param array $input The input to run through the channel
     * @return Ruth_Channel
     */
    public function run(array $input)
    {
        foreach ($this->_fields as $name => $field) {
            if (isset($input[$name])) {
                $field->setValue($input[$name]);
            }

            $field->runFilters();
        }

        foreach ($this->_fields as $field) {
            $field->runValidators();
        }

        return $this;
    }

    /**
     * Sets the field values and optionally runs the filters.
     *
     * @param array $input The input to pass through the channel
     * @param boolean $runFilters Whether or not to run the filters after setting the values
     * @return Ruth_Channel
     */
    public function passThrough(array $input, $runFilters = false)
    {
        foreach ($this->_fields as $name => $field) {
            if (isset($input[$name])) {
                $field->setValue($input[$name]);
            }

            if ($runFilters) {
                $field->runFilters();
            }
        }

        return $this;
    }

    /**
     * Stores all field values into an array, and returns this array. If 
     * $inclusionList is not null, only those fields in $inclusionList
     * will be in the returned array.
     * 
     * @var null|string|array $inclusionList Values of fields in this list will be in the returned array
     * @return array
     */
    public function toArray($inclusionList=null)
    {
        $ret = array();

        if (!is_null($inclusionList)) {
            $inclusionList = (array) $inclusionList;

            foreach ($inclusionList as $fieldName) {
                if (isset($this->_fields[$fieldName])) {
                    $ret[$fieldName] = $this->_fields[$fieldName]->getValue();
                }
            }
        } else {
            foreach ($this->_fields as $name => $field) {
                $ret[$name] = $field->getValue();
            }
        }

        return $ret;
    }

    /**
     * Stores all field values into an array, and returns this array, excluding
     * any fields in the $exclusionList variable.
     * 
     * @var string|array $exclusionList Values of fields in this list will not be in the returned array
     * @return array
     */
    public function toArrayExcluding($exclusionList=array())
    {
        $ret = array();
        $exclusionList = (array) $exclusionList;

        foreach ($this->_fields as $name => $field) {
            if (in_array($name, $exclusionList)) {
                continue;
            }

            $ret[$name] = $field->getValue();
        }

        return $ret;
    }

    /**
     * Sets the channel and it's fields to a state where another
     * array can be run through using the run() method.
     *
     */
    public function reset()
    {
        foreach ($this->_fields as $field) {
            $field->reset();
        }
    }

    /**
     * Returns true if all fields in the channel are valid, false otherwise.
     *
     * @return boolean
     */
    public function isValid()
    {
        foreach ($this->_fields as $field) {
            if (!$field->isValid) {
                return false;
            }
        }

        return true;
    }
}
// vim: sw=4:ts=4:sts=4:et
